home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / VideoToolboxSources / GDVideo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-18  |  45.5 KB  |  1,307 lines  |  [TEXT/KAHL]

  1. /* 
  2. GDVideo.c
  3. Copyright © 1989-1993 Denis G. Pelli
  4.  
  5. Complete set of routines to control video drivers directly, bypassing
  6. QuickDraw's Color and Palette Managers. There is a separate function call for
  7. each of the Control and Status calls implemented by video drivers. They are
  8. described in Designing Cards and Drivers, 3rd Ed., chapter 9.
  9.  
  10. This file also contains several other helpful routines that deal with video
  11. device drivers. For background, read “Video synch”.
  12.  
  13. HOW TO LOAD THE CLUT:
  14.  
  15. Most people reading this file simply want to know how to load the clut. Here's
  16. the code fragment you need. (You may also want to look at the demo Grating.c)
  17.  
  18. #include "VideoToolbox.h"
  19. void main(void)
  20. {
  21.     int i,error,clutSize;
  22.     GDHandle device;
  23.     static ColorSpec table[256];// 256*4*2=2kB is a lot to put on the stack, so
  24.                                 // we say "static' to put it in the heap instead, 
  25.                                 // and you should too, unless you're going to take 
  26.                                 // special care not to overflow the stack.
  27.     Require(gestalt8BitQD);
  28.     device=GetScreenDevice(0);
  29.     GDSaveGamma(device);
  30.     GDUncorrectedGamma(device);    // Tell the driver to faithfully copy our colors into 
  31.                                 // the clut without any transformation.
  32.     clutSize=(**(**(**device).gdPMap).pmTable).ctSize+1;
  33.     for(i=0;i<clutSize;i++){
  34.         // Colors are specified as rgb triplets of 16-bit unsigned values.
  35.         // The hardware uses the most significant bits, typically 8, but possibly more.
  36.         // Here we compute a linear gray scale. 
  37.         // You could replace 0xffff by USHRT_MAX, defined in limits.h, if you would
  38.         // find that more readable.
  39.         table[i].rgb.red=table[i].rgb.green=table[i].rgb.blue=i*(long)0xffff/(clutSize-1);
  40.     }
  41.     GDSetEntriesByType(device,0,clutSize-1,table);
  42. }
  43.  
  44. GETTING THE CLUT SIZE:
  45.  
  46. To use some of following calls you'll need to know how many entries there are in
  47. the clut, which I call the clutSize. The clut entries are numbered 0 to
  48. clutSize-1. E.g. in 4-bit mode there are 16 entries, numbered 0 to 15; in 8-bit
  49. mode there are 256 entries, numbered 0 to 255. QuickDraw keeps the current clut
  50. size, minus 1, in the ctSize field, which you can access as follows:
  51.  
  52. clutSize=(**(**(**device).gdPMap).pmTable).ctSize+1;
  53.  
  54. However, if you might have changed the pixel size without informing QuickDraw,
  55. i.e. by calling GDSetMode, then you should get the clut size by calling
  56. GDClutSize():
  57.  
  58. clutSize=GDClutSize(device);
  59.  
  60. THE MOST USEFUL CALLS:
  61.  
  62. short GDClutSize(GDHandle device);
  63. Returns the number of entries in the video driver's clut in the current video mode 
  64. (i.e. current pixel size).
  65.  
  66. OSErr GDRestoreDeviceClut(GDHandle device);
  67. Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
  68. since System 6.05. However, I find that Apple's routine sometimes does nothing,
  69. whereas this routine always works. Passing a NULL argument causes restoration of
  70. cluts of all video devices.
  71.  
  72. OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table);
  73. Checks the (**device).gdType field and calls GDSetEntries, GDDirectSetEntries,
  74. or nothing, as appropriate.
  75.  
  76. OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
  77.     ,ColorSpec *table);
  78. Calls GDSetEntriesByType() while the processor priority has been temporarily raised to 7.
  79.  
  80. OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table);
  81. Does a low-level setEntries Control call to the video card's driver, loading any
  82. number of clut entries with arbitrary rgb triplets. (Note that the driver will
  83. transform your rgb triplets via the gamma table before loading them into the
  84. clut; so call GDUncorrectedGamma first.) "device" specifies which video device's
  85. clut you want to load. "start" is either in the range 0 to clutSize-1,
  86. indicating which clut entry to load first (in "sequence mode"), or is -1
  87. (requesting "index mode"). "count" is the number of entries to be modified,
  88. minus 1. "table" is a ColorSpec array (not a ColorTable) containing the rgb
  89. triplets that you want to load into the clut. In sequence mode "start" must be
  90. in the range 0 to clutSize-1, the i-th element of table corresponds to the
  91. i+start entry in the clut, and the "value" field of each element of table is
  92. ignored. In index mode "start" must be -1, and the "value" field of each element
  93. of table indicates which clut entry to load it into. The arguments start,
  94. count, and table are the same as for the Color Manager call SetEntries(),
  95. documented in Inside Macintosh V-143. (Most drivers wait for blanking before
  96. modifying the clut. For a full discussion, read the "Video synch" file.) You may
  97. also want to look at the file SetEntriesQuickly.c, which provides the
  98. functionality of GDSetEntries and GDDirectSetEntries, but bypasses the video
  99. driver to access the hardware directly.
  100.  
  101. OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table);
  102. If your pixel depth is >8 then the setEntries call is disabled, and you must use
  103. this instead of GDSetEntries().
  104.  
  105. OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table);
  106. This is much as you'd expect after reading GDSetEntries above. Note that unless
  107. the gamma table is linear, the values returned may not be the same as those
  108. originally passed by GDSetEntries. So call GDUncorrectedGamma first. Try the demo 
  109. TimeVideo.
  110.  
  111. OSErr GDUncorrectedGamma(GDHandle device);
  112. Asks GDSetGamma to load a linear gamma table, i.e. no correction. (The
  113. gamma correction implemented by table-lookup in the video driver is too
  114. crude for experiments that want accurate luminance control.)
  115.  
  116. OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle);
  117. Returns a pointer to the Gamma table in the specified video device. (I.e. you
  118. pass it a pointer to your pointer, a handle, which it uses to load your
  119. pointer.)
  120.  
  121. OSErr GDSetGamma(GDHandle device, GammaTbl *gamma);
  122. Loads a Gamma table into the specified video device. The video driver will make
  123. a copy of your table. You can discard your table after making this call. Note
  124. that the video driver only uses the gamma table when performing SetEntries, i.e.
  125. when actually loading the clut. The structure of the gamma table is (finally!)
  126. documented in Designing Cards and Drivers, 3rd edition, pages 215-216. Beware of
  127. a discrepancy between the documentation and the definition in QuickDraw.h:
  128. gFormulaData is described as a byte array in the text, but is declared as a
  129. short array in the QuickDraw.h header file.
  130.  
  131. OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr);
  132. It tells you the current mode, page of video memory, and the base address of that
  133. page.
  134.  
  135. OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr);
  136. You tell it what mode and video page you want. It sets 'em and returns the base
  137. address of that page in video memory. Also, because Apple said so, the video
  138. driver sets the entire clut to 50% gray. Note that this changes things behind
  139. QuickDraw's back. For most applications life will be simpler if you overtly ask
  140. QuickDraw to take charge by calling Apple's SetDepth() instead of GDSetMode().
  141. Apple's Video.h header file defines these names:
  142. bits    colors        mode    name
  143. --------------------------------------
  144. 1        2            0x80    oneBitMode
  145. 2        4            0X81    twoBitMode
  146. 4        16            0X82    fourBitMode
  147. 8        256            0X83    eightBitMode
  148. 16        "thousands"    0X84    sixteenBitMode
  149. 32        "millions"    0X85    thirtyTwoBitMode
  150.  
  151. char *GDModeName(short mode);
  152. Returns the name, from the above table, as a C string. The returned name either 
  153. corresponds to the supplied mode, or is "undefined mode".
  154.  
  155. int GDVersion(GDHandle device)
  156. Returns the version number of the driver. From the first word-aligned word after
  157. the name string. This is quick.
  158.  
  159. unsigned char *GDName(GDHandle device);
  160. Returns a pointer to the name of the driver (a pascal string). This is quick.
  161. The string is part of the driver itself, so don't try to dispose of it.
  162.  
  163. char *GDCardName(GDHandle device);
  164. Returns the address of a C string containing the name of the video card. You
  165. should call DisposPtr() on the returned string when you no longer need it.
  166. Takes about 1.5 ms because Apple's slot routines are very slow. 
  167.  
  168. LESS USEFUL CALLS:
  169.  
  170. VideoDriver *GDDriverAddress(GDHandle device);
  171. Returns a pointer to the driver, whether in ROM or RAM. Neither this prototype
  172. nor the definition of the VideoDriver structure are included in VideoToolbox.h.
  173.  
  174. PatchMacIIciVideoDriver();
  175. Fixes a bug in the Mac IIci built-in video driver that would crash GDGetEntries.
  176. The patch persists until reboot. It is unlikely that you will need to call this
  177. explicitly, PatchMacIIciVideoDriver() is automatically invoked the first time
  178. GDGetEntries is called. The Mac IIci built-in video driver
  179. (.Display_Video_Apple_RBV1 driver, version 0) has a bug that causes it to crash
  180. if you try to do a GetEntries Status request. PatchMacIIciVideoDriver() finds
  181. and patches the copy of the buggy driver residing in memory. (If the driver is
  182. ROM-based it first moves it to RAM, and then patches that.) Only two
  183. instructions are modified, to save & restore more registers. This fix persists
  184. only until the next reboot. If the patch is successfully applied the version
  185. number is increased by 100.
  186.  
  187. OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr);
  188. Called "GetBaseAddr" in Designing Cards and Drivers, 3rd Ed. You tell it which
  189. page of video memory you're interested in (in the current video mode). It tells
  190. you the base address of that page.
  191.  
  192. OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr);
  193. Called "GetPages" in Designing Cards and Drivers, 3rd Ed. You tell it which mode
  194. you're interested in. It tells you how many pages of video ram are available in 
  195. that mode.
  196.  
  197. OSErr GDGetGray(GDHandle device,Boolean *flagPtr);
  198. Get gray flag. 0 for color. 1 if all colors mapped to luminance-equivalent gray tones.
  199.  
  200. OSErr GDSetGray(GDHandle device,Boolean flag);
  201. Set gray flag. 0 for color. 1 if all colors mapped to luminance-equivalent gray tones.
  202.  
  203. OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr);
  204. Get flag. 1 if VBL interrupts of this card are enabled. 0 if disabled. 
  205.  
  206. OSErr GDSetInterrupt(GDHandle device,Boolean flag);
  207. Set flag to 1 to enable VBL interrupts of this card, or 0 to disable. 
  208.  
  209. OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
  210. It tells you what the default mode is. I'm not sure what this means.
  211.  
  212. OSErr GDSetDefaultMode(GDHandle device,short mode);
  213. Supposedly, you tell it what mode you want to start up with when you reboot.
  214. (I've never been able to get this to work. No error and no effect. Perhaps I've
  215. misunderstood its purpose.)
  216.  
  217. OSErr GDGrayPage(GDHandle device,short page);
  218. Called "GrayScreen" in Designing Cards and Drivers, 3rd Ed. Fills the specified
  219. page with gray, i.e. the dithered desktop pattern. I'm not aware of any
  220. particular advantage in using this instead of FillRect(). Designing Cards and
  221. Drivers, 3rd Edition, Chapter 9, says that for direct devices, i.e. >8 bit
  222. pixels, the driver will also load a linear, gamma-corrected, gray color table.
  223.  
  224. OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr);
  225. Initialize the video card to its startup state, usually 1 bit per pixel. Returns
  226. the parameters of that state.
  227.  
  228. OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
  229. Uses low-level PBControl() call to implement a "Control()" call that works! 
  230. I don't know why this wasn't discussed in Apple Tech Note 262.
  231.  
  232. OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
  233. Uses low-level PBStatus() call to implement a "Status()" call that works! The
  234. need for this is explained in Apple Tech Note 262, which was issued in response
  235. to my bug report in summer of '89.
  236.  
  237. NOTES:
  238.  
  239. Several bugs in version 2 (in System ≤6.03) of the video driver for Apple's
  240. "Toby Frame Buffer" video card that affected use of the GetGamma call were
  241. fixed in version 3 (in System 6.04), apparently in response to my bug report to
  242. Apple.
  243.  
  244. The control/status-call parameter in a GDControl or GDStatus call specifies what
  245. you want done. See Designing Cards and Drivers, 3rd Edition, Chapter 9. Note
  246. that sometime around 1990 Apple renamed some of the Control and Status calls,
  247. giving them names that better reflect their function. I followed suit. However,
  248. Apple neglected to update their book, Designing Cards and Drivers, now in its
  249. 3rd edition. Fortunately, they define both names in their Video.h header file.
  250. To avoid confusion, here are the equivalences. Note that "csc" stands for 
  251. "Control and Status Call"
  252. Control call:
  253.  cscGrayPage =  cscGrayScreen = 5
  254. Status calls:
  255.  cscGetPageCnt = cscGetPages = 4
  256.  cscGetPageBase = cscGetBaseAddr = 5
  257.  
  258. Based on:
  259. Inside Macintosh-II (Device Manager)
  260. Designing Cards and Drivers, 3rd Edition, Chapter 9.
  261. Tech Note 262: "Controlling Status Calls"
  262. "GreyScale Information" from AppleLink "Apple Technical Info"
  263. "Video Configuration ROM Software Specification" also from AppleLink,
  264. in "Developer Tech Support:Macintosh:32 Bit QuickDraw"
  265.  
  266. HISTORY:
  267. 9/29/89     dgp    added caution above that successive calls to SetEntries may be on one 
  268.                 frame.
  269. 11/21/89    dgp    corrected mode list: 0x80... as pointed out by Chuck Stein
  270. 11/30/89    dgp    added note above from Don Kelly.
  271.                 Added cautionary note above about GDSetEntries().
  272. 3/1/90        dgp    updated comments.
  273. 3/3/90        dgp    included Video.h instead of defining VDSetEntryRecord and VDPageInfo.
  274. 3/20/90        dgp    made compatible with MPW C.
  275. 3/23/90        dgp    changed char to unsigned char in VDDefModeRec
  276.                 and VDFlagRec to prevent undesirable sign extension.
  277. 4/2/90        dgp    Deleted mode argument from GDGrayScreen().
  278. 7/28/90        dgp Fixed stack overflow in GDGrayScreen, by declaring SysEnvRec static.
  279. 10/20/90    dgp    Apple has renamed the control and status calls, so I followed suit:
  280.                 •GDGetPageBase replaces GDGetBaseAddr
  281.                 •GDReset replaces GDInit
  282.                 •GDGrayPage replace GDGrayScreen
  283.                 •GDGetPageCnt replaces GDGetPages
  284. 2/12/91        dgp Discovered that the old bug in GDGrayPage is still present in System
  285.                 6.05, so I removed the conditional around the bug fix. TestGDVideo
  286.                 now works with: Mac II Video Card, Mac II Display Card (4•8), 
  287.                 Mac IIci Built-in Video, TrueVision NuVista, Mac IIsi Built-in Video.
  288. 7/22/91        dgp    Changed definition of csGTable from GammaTblPtr to Ptr, 
  289.                 to conform with MPW C.
  290. 8/24/91        dgp    Made compatible with THINK C 5.0.
  291. 10/22/91    dgp    With help from Bart Farell, converted all functions headers to
  292.                 ANSI style.
  293. 8/26/92        dgp    added a few miscellaneous comments
  294.                 In all routines, *baseAddrPtr is now set only if baseAddrPtr is not NULL.
  295. 10/10/92    dgp    Added GDRestoreDeviceClut(). Removed obsolete support for THINK C 4.
  296. 11/30/92    dgp corrected error in comment documenting values of argument to GDSetGray().
  297.                 Set flag to zero for color,1 for luminance-mapped gray.
  298. 12/9/92        dgp    Enhanced GDRestoreDeviceClut(). Passing a NULL argument now requests
  299.                 application to all devices. I only just learned that Apple's
  300.                 RestoreDeviceClut() behaves in this way.
  301. 12/15/92    dgp Renamed GDRestoreDeviceClut to GDRestoreDeviceClut to be consistent
  302.                 with Apple's capitalization of RestoreDeviceClut.
  303. 12/16/92    dgp Updated comments to be consistent with 3rd edition of Designing Cards
  304.                 and Drivers. •Renamed myGDHandle to "device", which is easier to read.
  305.                 Renamed GDLinearGamma to GDUncorrectedGamma, and generalized it
  306.                 to work with any size DAC. For compatibility with old programs
  307.                 VideoToolbox.h now has a #define statement making GDLinearGamma
  308.                 an alias for GDUncorrectedGamma.
  309. 12/30/92    dgp Updated some of the comments. Eliminated Files.h.
  310. 12/30/92    dgp Use GDClutSize() to determine clut size.
  311. 1/4/93        dgp In GDClutSize, check for fixedType.
  312. 1/5/93        dgp In GDClutSize, only set last clut entry.
  313.                 Added PatchMacIIciVideoDriver() to the end of this file, and 
  314.                 automatically invoke it the first time GDGetEntries is called.
  315. 1/6/93        dgp    Cleaned up GDClutSize. It now seems to work correctly in the direct modes.
  316. 1/18/93    dgp    Spruced up the documentation.
  317.             •Added all the code formerly in GDIdentify.c, but omitted the obsolete 
  318.             function GDIdentify(), which simply printed GDName() and GDVersion.
  319.             •Enhanced GDGetEntries() to avoid calling drivers that are known
  320.             to crash, returning a statusErr instead.
  321.             •Recoded PatchMacIIciVideoDriver() so as not to call GetScreenDevice.c.
  322. 2/1/93    dgp    Fixed endless loop in PatchMacIIciVideoDriver. Enhanced to deal with
  323.             ROM-based drivers as well.
  324. 2/5/93    dgp    Expanded the documentation of GDSetEntries above, and supplied sample
  325.             code showing how to load the clut.
  326. 2/7/93    dgp Fixed endless loop in PatchMacIIciVideoDriver.
  327. 2/20/93    dgp    Fixed bug in GDUncorrectedGamma() that was causing TestCluts to fail
  328.             for video devices that have nonstandard gamma tables. 
  329. 3/18/93    dgp    Fixed divide by zero error in GDClutSize.
  330. */
  331.  
  332. #include "VideoToolbox.h"
  333. #include <assert.h>
  334. #include <Errors.h>
  335. #include <ROMDefs.h>
  336. #define dRAMBased 0x0040
  337.  
  338. typedef struct VDFlagRec {
  339.     unsigned char flag;
  340.     char pad;
  341. } VDFlagRec;
  342.  
  343. typedef struct VDDefModeRec{
  344.     unsigned char spID;
  345.     char pad;
  346. } VDDefModeRec;
  347.  
  348. typedef struct {
  349.     short flags;
  350.     short blanks[3];
  351.     short open;
  352.     short prime;
  353.     short control;
  354.     short status;
  355.     short close;
  356.     Str255 name;
  357. } VideoDriver;
  358. VideoDriver *GDDriverAddress(GDHandle device);
  359.  
  360. typedef struct {
  361.     short csCode;        // control code
  362.     short length;        // total parameter block bytes
  363.     char param[];        // control call data
  364. } ScrnCtl;
  365.  
  366. typedef struct {
  367.     short spDrvrHw;        // Slot Manager ID
  368.     short slot;            // Number of slot
  369.     long dCtlDevBase;    // Start of device's address space
  370.     short mode;            // screen characteristics
  371.     short flagMask;        // Which flag bits are used
  372.     short flags;        // device state: bit 0 = 0 = mono; bit 0 = 1 = color;
  373.                         // bit 11 =  1 = startup device; bit 15 = 1 = active
  374.     short colorTable;    // 'clut' id, default = -1
  375.     short gammaTable;    // Selects color intensity, default (MacII) = -1
  376.     Rect globalRect;    // global rectangle, main device topLeft = 0,0
  377.     short ctlCount;        // total control calls
  378.     ScrnCtl ctl;
  379. } Scrn;
  380.  
  381. typedef struct {
  382.     short scrnCount;    //Total devices
  383.     Scrn scrn;
  384. } Scrns;                // 'scrn' resource
  385.  
  386. Scrn **GDGetScrn(GDHandle device);
  387.  
  388. OSErr GDSetPageDrawn(GDHandle device,short page)
  389. // Select a page of video memory to draw into.
  390. // Untested.
  391. {
  392.     OSErr error;
  393.     short flags;
  394.     Ptr baseAddr;
  395.     static long qD=-1;
  396.  
  397.     if(qD==-1)Gestalt(gestaltQuickdrawVersion,&qD);
  398.     error=GDGetPageBase(device,page,&baseAddr);
  399.     if(error)return error;
  400.     if(qD>=gestalt32BitQD){
  401.         flags=GetPixelsState((**device).gdPMap);
  402.         LockPixels((**device).gdPMap);
  403.     }
  404.     (**(**device).gdPMap).baseAddr=baseAddr;
  405.     if(qD>=gestalt32BitQD){
  406.         GDeviceChanged(device);
  407.         SetPixelsState((**device).gdPMap,flags);
  408.     }
  409. }
  410.  
  411. OSErr GDSetPageShown(GDHandle device,short page)
  412. // Select a page of video memory for display.
  413. // Untested.
  414. {
  415.     OSErr error;
  416.     short mode;
  417.  
  418.     error=GDGetMode(device,&mode,NULL,NULL);
  419.     if(error)return error;
  420.     return GDSetMode(device,mode,page,NULL);
  421. }
  422.  
  423. short GDClutSize(GDHandle device)
  424. // Returns the number of entries in the clut, in the current mode.
  425. {
  426.     int error;
  427.     short mode;
  428.     long clutSize,i;
  429.     ColorSpec *table,white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  430.     static ColorSpec linearTable[256];
  431.  
  432.     // Method 1. If the current mode is "clutType" or "fixedType" we can just get the 
  433.     // device's ctSize, provided we haven't bypassed QuickDraw to change 
  434.     // the mode.
  435.     clutSize=1+(**(**(**device).gdPMap).pmTable).ctSize;
  436.     // Method 2. Just in case the video mode has been changed
  437.     // by calling GDSetMode(), which bypasses QuickDraw, we'll
  438.     // compute the clut size from the mode that we get directly 
  439.     // from the driver.
  440.     error=GDGetMode(device,&mode,NULL,NULL);
  441.     if(mode<sixteenBitMode)clutSize=1<<(1<<(mode&7));    // clutType or fixedType
  442.     else {                                                // directType
  443.         // Method 3. For directType we resort to trial and error.
  444.         // Try 265,128,...
  445.         for(clutSize=256;clutSize>1;clutSize>>=1){
  446.             // Prepare linearTable[].
  447.             table=linearTable;
  448.             for(i=0;i<clutSize;i++){
  449.                 table->rgb.red=table->rgb.green=table->rgb.blue=(long)0xffff*i/(clutSize-1);
  450.                 table++;
  451.             }
  452.             if(mode<sixteenBitMode)
  453.                 error=GDSetEntries(device,clutSize-1,0,&black);
  454.             else error=GDDirectSetEntries(device,0,clutSize-1,linearTable);
  455.             if(!error)break;
  456.         }
  457.         if(clutSize==1)switch(mode){    // when all else fails, guess
  458.             case sixteenBitMode:
  459.                 clutSize=32;
  460.                 break;
  461.             case thirtyTwoBitMode:
  462.                 clutSize=256;
  463.                 break;
  464.         }
  465.     }
  466.     return clutSize;
  467. }
  468.  
  469. /*
  470. Nominally equivalent to Apple's RestoreDeviceClut(), which is only available
  471. since System 6.05. However, I find that Apple's routine sometimes does nothing,
  472. whereas this routine always works. Passing a NULL argument causes restoration of
  473. cluts of all video devices.
  474. */
  475. OSErr GDRestoreDeviceClut(GDHandle device)
  476. {
  477.     OSErr error,lastError;
  478.     short mode;
  479.     short count;
  480.     long i;
  481.  
  482.     // If NULL, then recursively call ourselves for each device.
  483.     if(device==NULL){
  484.         lastError=0;
  485.         device=GetDeviceList();
  486.         while(device!=NULL) {
  487.             if(TestDeviceAttribute(device,screenDevice))
  488.                 error=GDRestoreDeviceClut(device);
  489.             if(error)lastError=error;
  490.             device=GetNextDevice(device);
  491.         }
  492.         return lastError;
  493.     }
  494.     error=GDGetMode(device,&mode,NULL,NULL);
  495.     if(error)return error;
  496.     if(mode<sixteenBitMode){
  497.         count=(**(**(**device).gdPMap).pmTable).ctSize;
  498.         error=GDSetEntries(device,0,count
  499.             ,((**(**(**device).gdPMap).pmTable)).ctTable);
  500.     } else error=GDSetGamma(device,NULL);
  501.     return error;
  502. }
  503.  
  504. GammaTbl **SavedGammaTable[MAX_SCREENS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
  505.  
  506. OSErr GDSaveGamma(GDHandle device)
  507. {
  508.     GammaTbl *gamma;
  509.     OSErr error;
  510.     long size;
  511.     int i;
  512.  
  513.     if((*device)->gdType==fixedType)return 0;
  514.     i=GetScreenIndex(device);
  515.     if(i>=MAX_SCREENS)return 1;
  516.      error=GDGetGamma(device,&gamma);
  517.     if(error)return error;
  518.     size=gamma->gChanCnt*gamma->gDataCnt;
  519.     if(gamma->gDataWidth>8)size*=2;
  520.     size+=sizeof(GammaTbl)+gamma->gFormulaSize;
  521.     if(SavedGammaTable[i]!=NULL){
  522.         DisposeHandle((Handle)SavedGammaTable[i]);
  523.         SavedGammaTable[i]=NULL;
  524.     }
  525.     error=PtrToHand(gamma,(Handle *)&SavedGammaTable[i],size);
  526.     return error;
  527. }
  528.  
  529. OSErr GDRestoreGamma(GDHandle device)
  530. {
  531.     int i;
  532.     OSErr error;
  533.     
  534.     if((*device)->gdType==fixedType)return 0;
  535.     i=GetScreenIndex(device);
  536.     if(i>=MAX_SCREENS || SavedGammaTable[i]==NULL)return 1;
  537.     error=GDSetGamma(device,*SavedGammaTable[i]);
  538.     if(error)return error;
  539.     DisposeHandle((Handle)SavedGammaTable[i]);
  540.     SavedGammaTable[i]=NULL;
  541.     return 0;
  542. }    
  543.  
  544. OSErr GDGetDefaultGamma(GDHandle device,GammaTbl **gammaTbl)
  545. // Looks for a default gamma table in both places that Apple says to look,
  546. // but usually comes up empty handed.
  547. {
  548.     OSErr error;
  549.     int i,j,slot;
  550.     Scrn **scrn;
  551.     GammaTbl **gammaHandle,*gamma;
  552.     SpBlock spBlock;
  553.     Ptr ptr;
  554.  
  555.     gammaHandle=NULL;
  556.     if(CountResources('gama')>0){// Any 'gama' resources available in System file?
  557.         // Check to see if 'scrn' resource in System file specifies a 'gama' resource.
  558.         scrn=GDGetScrn(device);
  559.         if(scrn!=NULL && (**scrn).gammaTable!=-1)
  560.             gammaHandle=(GammaTbl **)GetResource('gama',(**scrn).gammaTable);
  561.         DisposeHandle((Handle)scrn);
  562.     }
  563.     if(gammaHandle!=NULL){
  564.         // Got gamma table from 'gama' resource.
  565.         gamma=(GammaTbl *)NewPtr(GetHandleSize((Handle)gammaHandle));
  566.         if(gamma==NULL)return MemError();
  567.         BlockMove(*gammaHandle,gamma,GetHandleSize((Handle)gammaHandle));
  568.         DisposeHandle((Handle)gammaHandle);
  569.     }else{
  570.         // Try to get this device's default gamma table from Slot Manager.
  571.         spBlock.spSlot=slot=GetDeviceSlot(device);
  572.         spBlock.spID=0;
  573.         spBlock.spExtDev=0;
  574.         spBlock.spTBMask=3;            // match only spCategory and spCType
  575.         spBlock.spCategory=catDisplay;
  576.         spBlock.spCType=typeVideo;    // this might be too restrictive, excludes LCD
  577.         do{
  578.             error=SNextTypeSRsrc(&spBlock);
  579.             if(error)return error;
  580.         }while(spBlock.spSlot!=slot || spBlock.spRefNum!=(**device).gdRefNum);
  581.         
  582.         if(0){
  583.             // Print sResource name
  584.             spBlock.spID=sRsrcName;
  585.             error=SGetCString(&spBlock);
  586.             printf("Slot resource “%s”\n",spBlock.spResult);
  587.             if(error)return error;
  588.             DisposePtr((Ptr)spBlock.spResult);
  589.         }
  590.         
  591.         // Look for gamma directory. Unfortunately many video devices don't have one.
  592.         spBlock.spID=sGammaDir;
  593.         error=SFindStruct(&spBlock);
  594.         if(error)return error;
  595.         
  596.         // Retrieve default gamma table
  597.         spBlock.spID=0x80;
  598.         error=SGetBlock(&spBlock);
  599.         if(error)return error;
  600.         gamma=(GammaTbl *)spBlock.spResult;
  601.         ptr=(Ptr)spBlock.spResult+6;
  602.         if(0)printf("Gamma table “%s”, %ld bytes\n",ptr,GetPtrSize((Ptr)gamma));
  603.         ptr+=strlen(ptr)+1;                // table is just past string
  604.         ptr=(Ptr)((long)(ptr+1)&~1L);    // round up to even address
  605.         i=ptr-(Ptr)gamma;
  606.         BlockMove(ptr,(Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
  607.         SetPtrSize((Ptr)gamma,GetPtrSize((Ptr)gamma)-i);
  608.     }
  609.     *gammaTbl=gamma;
  610.     return 0;
  611. }
  612.  
  613. Scrn **GDGetScrn(GDHandle device)
  614. // Returns handle to a copy of the specific scrn resource for this device,
  615. // or NULL if none.
  616. {
  617.     OSErr error=0;
  618.     int i,j;
  619.     Scrns **scrns;
  620.     Scrn *scrn,**scrnHandle;
  621.     ScrnCtl *ctl;
  622.     int scrnCount;
  623.     long size;
  624.     short mode;
  625.  
  626.     scrns=(Scrns **)GetResource('scrn',0);
  627.     if(ResError())return NULL;
  628.     HLockHi((Handle)scrns);
  629.     scrnCount=(**scrns).scrnCount;
  630.     scrn=&(**scrns).scrn;
  631.     for(i=0;i<scrnCount;i++){
  632.         if(0 && scrn->slot==GetDeviceSlot(device)){
  633.             printf("Slot %d,",scrn->slot);
  634.             printf("mode %d,",scrn->mode);
  635.             printf("colorTable %d,",scrn->colorTable);
  636.             printf("gammaTable %d,",scrn->gammaTable);
  637.             printf("%d control calls:",scrn->ctlCount);
  638.         }
  639.         ctl=&scrn->ctl;
  640.         for(j=0;j<scrn->ctlCount;j++){
  641.             if(0 && scrn->slot==GetDeviceSlot(device))printf(" %d",ctl->csCode);
  642.             ctl=(ScrnCtl *)((long)(ctl+1)+ctl->length);
  643.         }
  644.         if(0 && scrn->slot==GetDeviceSlot(device))printf("\n");
  645.         size=(long)ctl-(long)scrn;
  646.         if(scrn->slot==GetDeviceSlot(device))break;
  647.         scrn=(Scrn *)ctl;
  648.     }
  649.     if(i<scrnCount){
  650.         if(error)return NULL;
  651.         scrnHandle=(Scrn **)NewHandle(size);
  652.         BlockMove(scrn,*scrnHandle,size);
  653.     }else scrnHandle=NULL;
  654.     ReleaseResource((Handle)scrns);
  655.     return scrnHandle;
  656. }
  657.  
  658. OSErr GDUncorrectedGamma(GDHandle device)
  659. /*
  660. Loads a linear gamma table into the specified video device, to defeat the
  661. driver's attempt to do gamma correction.
  662.  
  663. According to Designing Cards and Drivers, 3rd edition, passing a NULL
  664. GammaTblPtr instructs the driver to create a linear table. So you may wish to
  665. just call GDSetGamma(device,NULL) instead of calling GDUncorrectedGamma(device).
  666. However, I'm not sure how old that rule is, and don't know if all the older
  667. drivers support it. Therefore the following code first examines the current
  668. gamma table, and, if it's a plain vanilla table, as described in Designing Cards
  669. and Drivers, then we write in the desired linear table. If it's fancy (or if the
  670. driver doesn't support GDGetGamma) then we pass a NULL pointer, letting the driver
  671. create the new table. Hopefully this will work with all drivers.
  672. */
  673. {
  674.     OSErr error;
  675.     GammaTbl *gamma=NULL;
  676.     int i;
  677.     char *gData;
  678.  
  679.     error=GDGetGamma(device,&gamma);
  680.     if(!error && gamma->gVersion==0 && gamma->gChanCnt==1 && gamma->gDataWidth<=8){
  681.         // Overwrite the standard table
  682.         gData = (char *)&gamma->gFormulaData+gamma->gFormulaSize;
  683.         for(i=0;i<gamma->gDataCnt;i++)gData[i]=i;
  684.     }else{
  685.         // A fancy table implies a new driver, so let it do the work.
  686.         gamma=NULL;
  687.     }
  688.     error=GDSetGamma(device,gamma);
  689.     return error;
  690. }
  691.  
  692. OSErr GDReset(GDHandle device, short *modePtr, short *pagePtr, Ptr *baseAddrPtr)
  693. /*
  694. Initialize the video card to its startup state, usually 1 bit per pixel. Returns
  695. the parameters of that state.
  696. */
  697. {
  698.     VDPageInfo myVDPageInfo;
  699.     OSErr error;
  700.  
  701.     if(device==NULL) return controlErr;
  702.     myVDPageInfo.csMode= *modePtr;
  703.     myVDPageInfo.csPage= *pagePtr;
  704.     error=GDControl((*device)->gdRefNum,cscReset,(Ptr) &myVDPageInfo);
  705.     *modePtr=myVDPageInfo.csMode;
  706.     *pagePtr=myVDPageInfo.csPage;
  707.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  708.     return error;
  709. }
  710.  
  711. /* KillIO doesn't do anything, so I didn't bother to implement it. */
  712.  
  713. OSErr GDSetMode(GDHandle device,short mode,short page,Ptr *baseAddrPtr)
  714. {
  715.     VDPageInfo myVDPageInfo;
  716.     OSErr error;
  717.  
  718.     if(device==NULL) return controlErr;
  719.     myVDPageInfo.csMode=mode;
  720.     myVDPageInfo.csPage=page;
  721.     error=GDControl((*device)->gdRefNum,cscSetMode,(Ptr) &myVDPageInfo);
  722.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  723.     return error;
  724. }
  725.  
  726. OSErr GDSetEntriesByType(GDHandle device,short start,short count,ColorSpec *table)
  727. // Calls GDSetEntries or GDDirectSetEntries or nothing, as appropriate.
  728. // Assumes that the GDevice record is valid, i.e. that the user has not
  729. // called GDSetMode.
  730. {
  731.     switch((*device)->gdType){
  732.     case fixedType:
  733.         return statusErr;
  734.     case clutType:
  735.         return GDSetEntries(device,start,count,table);
  736.     case directType:
  737.         return GDDirectSetEntries(device,start,count,table);
  738.     }
  739. }
  740.  
  741. OSErr GDSetEntriesByTypeHighPriority(GDHandle device,short start,short count
  742.     ,ColorSpec *table)
  743. {
  744.     char priority;
  745.     OSErr error;
  746.  
  747.     priority=7;
  748.     SwapPriority(&priority);
  749.     error=GDSetEntriesByType(device,start,count,table);
  750.     SwapPriority(&priority);
  751.     return error;
  752. }
  753.  
  754. OSErr GDSetEntries(GDHandle device,short start,short count,ColorSpec *table)
  755. {
  756.     VDSetEntryRecord myVDSetEntryRecord;
  757.     OSErr error;
  758.  
  759.     if(device==NULL) return controlErr;
  760.     myVDSetEntryRecord.csStart=start;
  761.     myVDSetEntryRecord.csCount=count;
  762.     myVDSetEntryRecord.csTable=table;
  763.     error=GDControl((*device)->gdRefNum,cscSetEntries,(Ptr) &myVDSetEntryRecord);
  764.     return error;
  765. }
  766.  
  767. OSErr GDSetGamma(GDHandle device, GammaTbl *gamma)
  768. {
  769.     OSErr error;
  770.     VDGammaRecord myVDGammaRecord;
  771.  
  772.     myVDGammaRecord.csGTable=(Ptr)gamma;
  773.     error=GDControl((*device)->gdRefNum,cscSetGamma,(Ptr) &myVDGammaRecord);
  774.     return error;
  775. }
  776.  
  777. OSErr GDGrayPage(GDHandle device,short page)
  778. /*
  779. Called "GrayScreen" in Designing Cards and Drivers, 3rd Ed. Fills the specified
  780. page with gray, i.e. the dithered desktop pattern. I'm not aware of any
  781. particular advantage in using this instead of FillRect().
  782.  
  783. Designing Cards and Drivers, 3rd Edition, Chapter 9, says that for direct
  784. devices, i.e. >8 bit pixels, the driver will also load a linear,
  785. gamma-corrected, gray color table.
  786.  
  787. Contrary to the documentation, version 2 (in System 6.03) of the video driver
  788. for Apple's old video card requires that one supply the current mode as well.
  789. Supplying a garbage mode screwed up the screen and soon hung the software. So
  790. this code first obtains the current mode, and then supplies it in the GrayPage
  791. Control call.
  792. */
  793. {
  794.     VDPageInfo myVDPageInfo;
  795.     OSErr error;
  796.     /* The rest of the arguments are used soley for the bug fix */
  797.     short mode=0;        /* should be ignored, but isn't */
  798.     short actualPage;    /* ignored */
  799.     Ptr baseAddr;        /* ignored */
  800.  
  801.     if(device==NULL) return controlErr;
  802.     
  803.     /* Work around driver bug: get the mode */
  804.     error=GDGetMode(device,&mode,&actualPage,&baseAddr);
  805.     if(error)return error;
  806.     myVDPageInfo.csMode=mode;
  807.     
  808.     myVDPageInfo.csPage=page;
  809.     error=GDControl((*device)->gdRefNum,cscGrayPage,(Ptr) &myVDPageInfo);
  810.     return error;
  811. }
  812.  
  813. OSErr GDSetGray(GDHandle device,Boolean flag)
  814. /*
  815. Tells the driver whether you want colors (flag==0), or prefer that all colors be 
  816. mapped to luminance-equivalent gray tones? (flag==1).
  817. */
  818. {
  819.     VDFlagRec myVDFlagRec;
  820.     OSErr error;
  821.  
  822.     if(device==NULL) return controlErr;
  823.     myVDFlagRec.flag=flag;
  824.     error=GDControl((*device)->gdRefNum,cscSetGray,(Ptr) &myVDFlagRec);
  825.     return error;
  826. }
  827.  
  828. OSErr GDSetInterrupt(GDHandle device,Boolean flag)
  829. /*
  830. Set flag to 1 to enable VBL interrupts of this card, or 0 to disable. 
  831. I don't know when it's appropriate to call this.
  832. */
  833. {
  834.     VDFlagRec myVDFlagRec;
  835.     OSErr error;
  836.  
  837.     if(device==NULL) return controlErr;
  838.     myVDFlagRec.flag=flag;
  839.     error=GDControl((*device)->gdRefNum,cscSetInterrupt,(Ptr) &myVDFlagRec);
  840.     return error;
  841. }
  842.  
  843. OSErr GDDirectSetEntries(GDHandle device,short start,short count,ColorSpec *table)
  844. /*
  845. If your pixel depth is >8 then the setEntries Control call is disabled, and you use
  846. this instead. Except for that, GDDirectSetEntries is identical to GDSetEntries.
  847. */
  848. {
  849.     VDSetEntryRecord myVDSetEntryRecord;
  850.     OSErr error;
  851.  
  852.     if(device==NULL) return controlErr;
  853.     myVDSetEntryRecord.csStart=start;
  854.     myVDSetEntryRecord.csCount=count;
  855.     myVDSetEntryRecord.csTable=table;
  856.     error=GDControl((*device)->gdRefNum,cscDirectSetEntries,(Ptr) &myVDSetEntryRecord);
  857.     return error;
  858. }
  859.  
  860. OSErr GDSetDefaultMode(GDHandle device,short mode)
  861. /*
  862. Supposedly, you tell it what mode you want to start up with when you reboot.
  863. (I've never been able to get this to work. No error and no effect. Perhaps I've
  864. misunderstood its purpose.)
  865. */
  866. {
  867.     VDDefModeRec myVDDefModeRec;
  868.     OSErr error;
  869.  
  870.     if(device==NULL) return controlErr;
  871.     myVDDefModeRec.spID=mode;
  872.     error=GDControl((*device)->gdRefNum,cscSetDefaultMode,(Ptr) &myVDDefModeRec);
  873.     return error;
  874. }
  875.  
  876. OSErr GDGetMode(GDHandle device,short *modePtr,short *pagePtr,Ptr *baseAddrPtr)
  877. /*
  878. It tells you the current mode, page of video memory, and the base address of that
  879. page.
  880. */
  881. {
  882.     VDPageInfo myVDPageInfo;
  883.     OSErr error;
  884.  
  885.     if(device==NULL) return statusErr;
  886.     error=GDStatus((*device)->gdRefNum,cscGetMode,(Ptr) &myVDPageInfo);
  887.     if(modePtr!=NULL)*modePtr=myVDPageInfo.csMode;
  888.     if(pagePtr!=NULL)*pagePtr=myVDPageInfo.csPage;
  889.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  890.     return error;
  891. }
  892.  
  893. OSErr GDGetEntries(GDHandle device,short start,short count,ColorSpec *table)
  894. /*
  895. This is much as you'd expect after reading GDSetEntries above. Note that unless
  896. the gamma table is linear, the values returned may not be the same as those
  897. originally passed by GDSetEntries. So call GDUncorrectedGamma first. Note that
  898. version 2 (in System 6.03) of the video driver for Apple's old video card had a
  899. bug and did not support "indexed" mode, i.e. start==-1. This is fixed in System
  900. 6.05. Apple's .Display_Video_Apple_RBV1 v. 0 video driver (built-in to Mac IIci)
  901. crashes if you attempt to make this call. However, that's a thing of the past,
  902. because we now first patch the driver to fix the bug. Try the demo TimeVideo.
  903. */
  904. {
  905.     VDSetEntryRecord myVDSetEntryRecord;
  906.     OSErr error;
  907.     static int bugsPatched=0;
  908.     unsigned char *name;
  909.     int version;
  910.  
  911.     if(device==NULL)return statusErr;
  912.     if(!bugsPatched){
  913.         PatchMacIIciVideoDriver();
  914.         bugsPatched=1;
  915.     }
  916.  
  917.     // Contrary to Apple's rules, these drivers crash if we attempt
  918.     // a getEntries call. So let's be polite and instead simply return
  919.     // an error message indicating that this call is not available.
  920.     name=GDName(device);
  921.     version=GDVersion(device);
  922.     if(EqualString(name,"\p.Display_Video_Apple_RBV1",1,1) && version==0)return statusErr;
  923.     if(EqualString(name,"\p.Color_Video_Display",1,1) && version==9288)return statusErr;
  924.  
  925.     myVDSetEntryRecord.csStart=start;
  926.     myVDSetEntryRecord.csCount=count;
  927.     myVDSetEntryRecord.csTable=table;
  928.     error=GDStatus((*device)->gdRefNum,cscGetEntries,(Ptr) &myVDSetEntryRecord);
  929.     return error;
  930. }
  931.  
  932. OSErr GDGetPageCnt(GDHandle device,short mode,short *pagesPtr)
  933. /*
  934. Called "GetPages" in Designing Cards and Drivers, 3rd Ed. You tell it what mode
  935. you're interested in. It tells you how many pages of video ram are available.
  936. */
  937. {
  938.     VDPageInfo myVDPageInfo;
  939.     OSErr error;
  940.  
  941.     if(device==NULL) return statusErr;
  942.     myVDPageInfo.csMode=mode;
  943.     error=GDStatus((*device)->gdRefNum,cscGetPageCnt,(Ptr) &myVDPageInfo);
  944.     *pagesPtr=myVDPageInfo.csPage;
  945.     return error;
  946. }
  947.  
  948. OSErr GDGetPageBase(GDHandle device,short page,Ptr *baseAddrPtr)
  949. /*
  950. Called "GetBaseAddr" in Designing Cards and Drivers, 3rd Ed. You tell it what
  951. page of video memory you're interested in (in the current video mode). It tells
  952. you the base address of that page.
  953. */
  954. {
  955.     VDPageInfo myVDPageInfo;
  956.     OSErr error;
  957.  
  958.     if(device==NULL) return statusErr;
  959.     myVDPageInfo.csPage=page;
  960.     error=GDStatus((*device)->gdRefNum,cscGetPageBase,(Ptr) &myVDPageInfo);
  961.     if(baseAddrPtr!=NULL) *baseAddrPtr=myVDPageInfo.csBaseAddr;
  962.     return error;
  963. }
  964.  
  965. OSErr GDGetGray(GDHandle device,Boolean *flagPtr)
  966. // It tells you what the flag is set to. Do you want colors? (flag==0) Or do you
  967. // want all colors mapped to luminance-equivalent gray tones? (flag==1).
  968. {
  969.     VDFlagRec myVDFlagRec;
  970.     OSErr error;
  971.  
  972.     if(device==NULL) return statusErr;
  973.     error=GDStatus((*device)->gdRefNum,cscGetGray,(Ptr) &myVDFlagRec);
  974.     *flagPtr=myVDFlagRec.flag;
  975.     return error;
  976. }
  977.  
  978. OSErr GDGetInterrupt(GDHandle device,Boolean *flagPtr)
  979. // Get flag. 1 if VBL interrupts of this card are enabled. 0 if disabled. 
  980. {
  981.     VDFlagRec myVDFlagRec;
  982.     OSErr error;
  983.  
  984.     if(device==NULL) return statusErr;
  985.     error=GDStatus((*device)->gdRefNum,cscGetInterrupt,(Ptr) &myVDFlagRec);
  986.     *flagPtr=myVDFlagRec.flag;
  987.     return error;
  988. }
  989.  
  990. OSErr GDGetGamma(GDHandle device,GammaTbl **myGammaTblHandle)
  991. /*
  992. Returns a pointer to the Gamma table in the specified video device. (I.e. you
  993. pass it a pointer to your pointer, a handle, which it uses to load your
  994. pointer.)
  995.  
  996. Note that version 2 (in System ≤6.03) of the video driver for Apple's old video
  997. card does not support this call due to a bug in the driver code. The later
  998. versions of the driver (3, 4, and 5, in System 6.04 and later) work correctly.
  999. */
  1000. {
  1001.     OSErr error;
  1002.     VDGammaRecord myVDGammaRecord;
  1003.  
  1004.     if(device==NULL) return statusErr;
  1005.     myVDGammaRecord.csGTable=NULL;    // default address is NULL
  1006.     error=GDStatus((*device)->gdRefNum,cscGetGamma,(Ptr) &myVDGammaRecord);
  1007.     *myGammaTblHandle=(GammaTblPtr)myVDGammaRecord.csGTable;
  1008.     return error;
  1009. }
  1010.  
  1011. OSErr GDGetDefaultMode(GDHandle device,short *modePtr)
  1012. // It tells you what the default mode is. I'm not sure what this means.
  1013. {
  1014.     VDDefModeRec myVDDefModeRec;
  1015.     OSErr error;
  1016.  
  1017.     if(device==NULL) return statusErr;
  1018.     error=GDStatus((*device)->gdRefNum,cscGetDefaultMode,(Ptr) &myVDDefModeRec);
  1019.     *modePtr=(unsigned char)myVDDefModeRec.spID;
  1020.     return error;
  1021. }
  1022.  
  1023. OSErr GDControl(int refNum,int csCode,Ptr csParamPtr)
  1024. // Uses low-level PBControl() call to implement a "Control()" call that works! 
  1025. // I don't know why this wasn't discussed in Apple Tech Note 262.
  1026. {
  1027.     static CntrlParam myCntrlParam;
  1028.     OSErr error;
  1029.     
  1030.     myCntrlParam.ioCompletion=NULL;
  1031.     myCntrlParam.ioCRefNum=refNum;
  1032.     myCntrlParam.csCode=csCode;
  1033.     *((Ptr *) &myCntrlParam.csParam[0]) = csParamPtr;
  1034.     error=PBControl((ParmBlkPtr) &myCntrlParam,0);
  1035.     return error;
  1036. }
  1037.  
  1038. OSErr GDStatus(int refNum,int csCode,Ptr csParamPtr)
  1039. // Uses low-level PBStatus() call to implement a "Status()" call that works! The
  1040. // need for this is explained in Apple Tech Note 262, which was issued in response
  1041. // to my bug report in summer of '89.
  1042. {
  1043.     static CntrlParam myCntrlParam;
  1044.     OSErr error;
  1045.     
  1046.     myCntrlParam.ioCompletion=NULL;
  1047.     myCntrlParam.ioCRefNum=refNum;
  1048.     myCntrlParam.csCode=csCode;
  1049.     *((Ptr *) &myCntrlParam.csParam[0]) = csParamPtr;
  1050.     error=PBStatus((ParmBlkPtr) &myCntrlParam,0);
  1051.     return error;
  1052. }
  1053.  
  1054. char *GDModeName(short mode)
  1055. {
  1056.     switch(mode){
  1057.     case oneBitMode: return "oneBitMode";
  1058.     case twoBitMode: return "twoBitMode";
  1059.     case fourBitMode: return "fourBitMode";
  1060.     case eightBitMode: return "eightBitMode";
  1061.     case sixteenBitMode: return "sixteenBitMode";
  1062.     case thirtyTwoBitMode: return "thirtyTwoBitMode";
  1063.     default: return "undefined mode";
  1064.     }
  1065. }
  1066.  
  1067. #if 0
  1068. /*
  1069. From: absurd@apple.apple.com (Tim Dierks, software saboteur)
  1070. Date: Fri, 20 Nov 1992 00:38:14 GMT
  1071. Organization: MacDTS Marauders
  1072.  
  1073. Here's the right way to get the slot number of a monitor, given its
  1074. GDevice:  (as a bonus, this also gets the name of the card in
  1075. question; to just get the slot, chop off all the lines after the
  1076. *slot = ... line.
  1077. */
  1078.  
  1079. OSErr GetSlotAndName(GDHandle gDev,short *slot,char *name)
  1080. {   OSErr       err;
  1081.     short       refNum;
  1082.     SpBlock     spb;
  1083.     
  1084.     refNum = (**gDev).gdRefNum;    // video driver refNum for this GDevice
  1085.     *slot = (**(AuxDCEHandle)GetDCtlEntry(refNum)).dCtlSlot;
  1086.                                    // slot in which this video card sits
  1087.     spb.spSlot = *slot;         // In the slot we're interested in
  1088.     spb.spID = 0;
  1089.     spb.spExtDev = 0;
  1090.     spb.spCategory = 1;         // Get the board sResource
  1091.     spb.spCType = 0;
  1092.     spb.spDrvrSW = 0;
  1093.     spb.spDrvrHW = 0;
  1094.     spb.spTBMask = 0;
  1095.     err = SNextTypeSRsrc(&spb);
  1096.     if (err)return err;
  1097.     spb.spID = 2;               // Getting the description string
  1098.                                 // spSPointer was set up by SNextTypesResource
  1099.     err = SGetCString(&spb);
  1100.     if (err)return err;
  1101.     strcpy(name,(char *)spb.spResult);  // Get the returned string
  1102.     DisposPtr((Ptr)spb.spResult);    // Undocumented; we have to dispose of it
  1103.     c2pstr(name);
  1104.     return noErr;
  1105. }
  1106. #endif
  1107.  
  1108. char *GDCardName(GDHandle device)
  1109. /*
  1110. Returns the address of a C string containing the name of the video card. You
  1111. should call DisposPtr() on the returned address when you no longer need it.
  1112. Takes about 1.5 ms; I don't know why Apple's slot routines are so slow. This
  1113. routine sets up the flags in the SNextTypeSRsrc() call quite differently from
  1114. the above example, but I don't know if that matters, since I've been using this
  1115. routine for a year or so without any problems.
  1116. */
  1117. {
  1118.     AuxDCE **auxDCEHandle;
  1119.     SpBlock SPB,SPB1;
  1120.     
  1121.     if(device==NULL)return "";
  1122.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1123.     SPB.spSlot = (**auxDCEHandle).dCtlSlot;
  1124.     SPB.spCategory = 0;
  1125.     SPB.spCType =     0;
  1126.     SPB.spDrvrSW =     0;
  1127.     SPB.spDrvrHW =     1;
  1128.     SPB.spTBMask = 0xf;
  1129.     SPB.spID = 0;
  1130.     SPB.spExtDev = 0;
  1131.     if(SNextTypeSRsrc(&SPB) == noErr) {
  1132.         SPB1.spsPointer = SPB.spsPointer;
  1133.         SPB1.spID = 2;
  1134.         SGetCString(&SPB1);
  1135.         return (char *)SPB1.spResult;
  1136.     }
  1137.     else return "";
  1138. }
  1139.  
  1140. unsigned char *GDName(GDHandle device)
  1141. // Returns a pointer to the name of the driver (a pascal string). 
  1142. {
  1143.     VideoDriver *videoDriverPtr;
  1144.  
  1145.     if(device==NULL)return "\p";
  1146.     videoDriverPtr=GDDriverAddress(device);
  1147.     return videoDriverPtr->name;
  1148. }
  1149.  
  1150. int GDVersion(GDHandle device)
  1151. // Returns the version number of the driver. From the first word-aligned word after
  1152. // the name string.
  1153. {
  1154.     int version;
  1155.     unsigned char *bytePtr;
  1156.  
  1157.     if(device==NULL)return 0;
  1158.     bytePtr=GDName(device);
  1159.     bytePtr += 1+bytePtr[0];    /* go to end of Pascal string */
  1160.     bytePtr = (unsigned char *)((long)(bytePtr+1) & ~1);    // round up to word boundary
  1161.     version = *(short *)bytePtr;
  1162.     return version;
  1163. }
  1164.  
  1165. VideoDriver *GDDriverAddress(GDHandle device)
  1166. // Returns a pointer to the driver, whether in ROM or RAM. 
  1167. {
  1168.     AuxDCE **auxDCEHandle;
  1169.     VideoDriver *videoDriverPtr;
  1170.  
  1171.     if(device==NULL)return NULL;
  1172.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1173.     if((**auxDCEHandle).dCtlFlags & dRAMBased){
  1174.         /* RAM-based driver. */
  1175.         videoDriverPtr=*(VideoDriver **) (**auxDCEHandle).dCtlDriver;
  1176.     }
  1177.     else{
  1178.         /* ROM-based driver. */
  1179.         videoDriverPtr=(VideoDriver *) (**auxDCEHandle).dCtlDriver;
  1180.     }
  1181.     return videoDriverPtr;
  1182. }
  1183.  
  1184. /*
  1185. ROUTINE: PatchMacIIciVideoDriver
  1186. PURPOSE:
  1187. It is unlikely that you will need to call this explicitly, because it is called
  1188. automatically by GDGetEntries the first time it is invoked, and the sole purpose
  1189. of this routine is to fix a driver bug that would cause a crash when called by
  1190. GDGetEntries.
  1191.  
  1192. The Mac IIci built-in video driver (.Display_Video_Apple_RBV1 driver, version 0)
  1193. has a bug that causes it to crash if you try to do a getEntries Status request.
  1194. PatchMacIIciVideoDriver() will find and patch the memory-resident copy of the
  1195. buggy driver. Only two instructions are modified, to save and restore more
  1196. registers. This fix persists only until the next reboot. If the patch is
  1197. successfully applied the version number is increased from 0 to 100, to
  1198. distinguish it from versions 0 and 1.
  1199.  
  1200. A returned value of 1 indicates success: the needed patch was applied, either now
  1201. or previously. A return value of 0 indicates no patch was needed.
  1202.  
  1203. This patch is based on a comparison of the version 0 and 1 drivers used by the
  1204. Mac IIci and IIsi, respectively. The patch changes a pair of save and restore
  1205. operations (MOVEM.L to and from the stack) to save and restore registers D1 and
  1206. A1 as well as D4, A3, and A4. There are many other differences between versions
  1207. 0 and 1 of the driver, but this change is enough to keep the version 0 driver
  1208. from crashing when we attempt to read the clut by calling GDGetEntries.
  1209.  
  1210. The only change that the Mac operating system could possibly notice is that,
  1211. when we're done patching, we set the handle to be non-purgeable, since purging
  1212. and reloading the driver would eliminate the patch.
  1213.  
  1214. edward_de_Jong@bmug.org and Robert Savoy (SAVOY@RISVAX.ROWLAND.ORG) reported
  1215. that their Mac IIci computers' built-in video driver is ROM-based, so
  1216. PatchMacIIciVideoDriver was enhanced to deal with a ROM-based driver, by copying
  1217. the driver into RAM, making that the active driver, and patching it. Robert
  1218. Savoy reports that it works fine.
  1219.  
  1220. An alternative, permanent, solution is described in the file "Video synch":
  1221. upgrading to Apple's bug-free version 1 of the driver. However, that solution
  1222. only works if the Mac IIci has more than one monitor.
  1223.  
  1224. */
  1225.  
  1226. int PatchMacIIciVideoDriver(void)
  1227. {
  1228.     GDHandle device;
  1229.     int i;
  1230.     short *w;
  1231.     AuxDCE **auxDCEHandle;
  1232.     Handle handle;
  1233.     enum{badVersion=0};
  1234.     int error;
  1235.     long value;
  1236.     
  1237.     error=Gestalt(gestaltQuickdrawVersion,&value);
  1238.     if(error || value<gestalt8BitQD)return 0;    // need 8-bit quickdraw
  1239.     device = GetDeviceList();
  1240.     while(1) {
  1241.         if(device==NULL)return 0;
  1242.         if (!TestDeviceAttribute(device,screenDevice)
  1243.             || !TestDeviceAttribute(device,screenActive)){
  1244.             device=GetNextDevice(device);
  1245.             continue;
  1246.         }
  1247.         if(EqualString("\p.Display_Video_Apple_RBV1",GDName(device),1,1))
  1248.             switch(GDVersion(device)){
  1249.                 case badVersion:
  1250.                     break;
  1251.                 case badVersion+100:    // already patched
  1252.                     return 1;
  1253.                 default:
  1254.                     return 0;
  1255.         }else{
  1256.             device=GetNextDevice(device);
  1257.             continue;
  1258.         }
  1259.         break;
  1260.     }
  1261.     auxDCEHandle = (AuxDCE **) GetDCtlEntry((*device)->gdRefNum);
  1262.     
  1263.     // Move ROM-based driver into RAM.
  1264.     if(!((**auxDCEHandle).dCtlFlags & dRAMBased)){
  1265.         long bytes;
  1266.         VideoDriver *driver;
  1267.         
  1268.         driver=(VideoDriver *)(**auxDCEHandle).dCtlDriver;
  1269.         // Sometimes the word preceding the driver in ROM seems to be the
  1270.         // driver size, but not always, e.g. the built-in driver on the Mac IIsi.
  1271.         bytes=*((short *)driver-1);
  1272.         // Driver size unknown, guessing (generously) at twice the highest offset.
  1273.         bytes=driver->open;
  1274.         if(bytes<driver->prime)bytes=driver->prime;
  1275.         if(bytes<driver->control)bytes=driver->control;
  1276.         if(bytes<driver->status)bytes=driver->status;
  1277.         if(bytes<driver->close)bytes=driver->close;
  1278.         bytes*=2;
  1279.         // We know the Mac IIci driver size to be 1896
  1280.         // when ROM version is 124 rev. 1, but who knows for later ROMs?
  1281.         //bytes=1896;
  1282.         handle=NewHandleSys(bytes);
  1283.         if(handle==NULL)return 0;    // Insufficient room on System heap.
  1284.         HLockHi(handle);
  1285.         BlockMove((Ptr)driver,*handle,bytes);
  1286.         (**auxDCEHandle).dCtlDriver=(Ptr)handle;
  1287.         (**auxDCEHandle).dCtlFlags |= dRAMBased;        
  1288.     }
  1289.     
  1290.     // Patch RAM-based driver.
  1291.     handle=(Handle)(**auxDCEHandle).dCtlDriver;
  1292.     w=*(short **)handle;
  1293.     if(w[0x51e/2]!=0x818 || w[0x590/2]!=0x1810 || w[0x2c/2]!=badVersion){
  1294.         printf("PatchMacIIciVideoDriver error.\n");
  1295.         return 0;
  1296.     }
  1297.     w[0x51e/2]=0x4858;
  1298.     w[0x590/2]=0x1a12;
  1299.     w[0x2c/2]+=100;                                    // Change version number.
  1300.     if(w[0x51e/2]!=0x4858 || w[0x590/2]!=0x1a12){
  1301.         printf("PatchMacIIciVideoDriver error.\n");
  1302.         return 0;
  1303.     }
  1304.     HNoPurge(handle);
  1305.     return 1;
  1306. }
  1307.